#include "stdafx.h"
#include "SSAO.h"

#include "GlobalAppData.h"
#include "InputLayouts.h"
#include "LoadTexture.h"
#include "LoadShader.h"
#include "Renderer.h"

using namespace mgfx;

SSAO::SSAO(bool fullRes)
{
	mFullRes = fullRes;

	mScreenWidth = mcore::GlobalAppData::Instance.ScreenWidth;
	mScreenHeight = mcore::GlobalAppData::Instance.ScreenHeight;

	if(fullRes)
	{
		mEffectWidth = mScreenWidth;
		mEffectHeight = mScreenHeight;
	}
	else
	{
		mEffectWidth = mScreenWidth / 2;
		mEffectHeight = mScreenHeight / 2;
	}



	mRenderTarget1 = Renderer::Instance->addRenderTarget(mEffectWidth, mEffectHeight, DXGI_FORMAT_R8_UNORM);
	mRenderTarget0 = Renderer::Instance->addRenderTarget(mEffectWidth, mEffectHeight, DXGI_FORMAT_R8_UNORM);
	mDepthRT = Renderer::Instance->addRenderTarget(mEffectWidth, mEffectHeight, DXGI_FORMAT_R16_FLOAT);

	FinalRenderTarget = mRenderTarget0;

	mTexRandom4x4 = loadTextureFromFile("random4x4.png");

	ShaderDefine msaaDefine("MSAA", "1");
	bool msaa = mcore::GlobalAppData::Instance.MSAASamples > 1;

	std::vector<ShaderDefine> defines;
	defines.push_back(ShaderDefine("VERTICAL", "1"));
	if(msaa) defines.push_back(msaaDefine);
	mShaderBlurV = loadShaderFromFile("Blur.fx", defines);

	defines.clear();
	defines.push_back(ShaderDefine("HORIZONTAL", "1"));
	if(msaa) defines.push_back(msaaDefine);
	mShaderBlurH = loadShaderFromFile("Blur.fx", defines);

	mShaderSSAO = msaa ? loadShaderFromFile("SSAO.fx", msaaDefine) : loadShaderFromFile("SSAO.fx");
	mShaderDepthDownsample = msaa ? loadShaderFromFile("DepthDownsample.fx", msaaDefine) : loadShaderFromFile("DepthDownsample.fx");
}

SSAO::~SSAO()
{
}

void mgfx::SSAO::render(const XMFLOAT4X4 & projectionMatrix, TextureID depthRT, TextureID normalRT, TextureID specPowRT)
{
	float screenResolution[] = { (float)mScreenWidth, (float)mScreenHeight };
	float resolution[] = { (float)mEffectWidth, (float)mEffectHeight };
	float pixelSize[] = { 1.0f / mEffectWidth, 1.0f / mEffectHeight };

	Renderer * rnd = Renderer::Instance;

	Viewport oldVp = rnd->getViewport();

	rnd->setViewportFromTextureDim(mRenderTarget0);

	// depth downsample pass
	if(!mFullRes)
	{
		rnd->setRenderTargets(1, &mDepthRT, Renderer::TEXTURE_NONE);

		rnd->setShader(mShaderDepthDownsample);

		rnd->setShaderResource("gDepthTex", depthRT);
		rnd->setShaderFloatVector("gGBufferRes", screenResolution);

		rnd->drawFullScreenQuad();

		rnd->setShaderResource("gDepthTex", Renderer::TEXTURE_NONE);
		rnd->applyConstantsWithoutDrawing();
	}
	


	// generation pass
	XMMATRIX proj, invProj;
	proj = XMLoadFloat4x4(&projectionMatrix);
	XMVECTOR tmp;
	invProj = XMMatrixInverse(&tmp, proj);

	rnd->setRenderTargets(1, &mRenderTarget0, Renderer::TEXTURE_NONE);

	rnd->setShader(mShaderSSAO);
	rnd->setShaderMatrix("gProj", (float*)&proj);
	rnd->setShaderMatrix("gProjInv", (float*)&invProj);
	rnd->setShaderFloatVector("gBufferRes", resolution);
	rnd->setShaderFloatVector("gGBufferRes", screenResolution);
	rnd->setShaderResource("gDepthTexture", mFullRes ? depthRT : mDepthRT);
	rnd->setShaderResource("gNormal_SpecP_Tex", normalRT);
	rnd->setShaderResource("gRandom4x4", mTexRandom4x4);

	rnd->drawFullScreenQuad();

	rnd->setShaderResource("gDepthTexture", Renderer::TEXTURE_NONE);
	rnd->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rnd->applyConstantsWithoutDrawing();

	// vertical blur
	rnd->setRenderTargets(1, &mRenderTarget1, Renderer::TEXTURE_NONE);
	rnd->setShader(mShaderBlurV);
	rnd->setShaderFloatVector("gPixelSize", pixelSize);
	rnd->setShaderFloatVector("gGBufferRes", screenResolution);
	rnd->setShaderResource("gSSAOMap", mRenderTarget0);
	rnd->setShaderResource("gDepthTexture", mFullRes ? depthRT : mDepthRT);
	rnd->setShaderResource("gNormal_SpecP_Tex", normalRT);
	rnd->drawFullScreenQuad();
	
	// unbound resources
	rnd->setShaderResource("gSSAOMap", Renderer::TEXTURE_NONE);
	rnd->setShaderResource("gDepthTexture", Renderer::TEXTURE_NONE);
	rnd->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rnd->applyConstantsWithoutDrawing();

	// horizontal blur
	rnd->setRenderTargets(1, &mRenderTarget0, Renderer::TEXTURE_NONE);
	rnd->setShader(mShaderBlurH);
	rnd->setShaderFloatVector("gPixelSize", pixelSize);
	rnd->setShaderFloatVector("gGBufferRes", screenResolution);
	rnd->setShaderResource("gSSAOMap", mRenderTarget1);
	rnd->setShaderResource("gDepthTexture", mFullRes ? depthRT : mDepthRT);
	rnd->setShaderResource("gNormal_SpecP_Tex", normalRT);
	rnd->drawFullScreenQuad();

	// Unbound resources
	rnd->setShaderResource("gSSAOMap", Renderer::TEXTURE_NONE);
	rnd->setShaderResource("gDepthTexture", Renderer::TEXTURE_NONE);
	rnd->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rnd->applyConstantsWithoutDrawing();

	rnd->setViewport(oldVp);
}
